using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using Microsoft.Xaml.Behaviors;

namespace WPFTemplateLib.Behaviors
{
	/// <summary>
	/// 用于行为库的可改变附加属性的动作
	/// https://www.bilibili.com/video/BV1aM4m127HT/
	/// </summary>
	/// <example>
	/// <![CDATA[
	///	<b:Interaction.Triggers>
	///		<b:EventTrigger EventName="MouseDown">
	///			<lib:ChangeAttachedPropertyAction ClassType="smartAdorner:SmartAdorner" PropertyName="Visible" Value="True"/>
	///		</b:EventTrigger>
	///	</b:Interaction.Triggers>
	/// ]]>
	/// </example>
	public class ChangeAttachedPropertyAction : TargetedTriggerAction<UIElement>
	{
		/// <summary>
		/// 附加属性所属的类
		/// </summary>
		public Type ClassType
		{
			get { return (Type)GetValue(ClassTypeProperty); }
			set { SetValue(ClassTypeProperty, value); }
		}
		public static readonly DependencyProperty ClassTypeProperty = DependencyProperty.Register(
			nameof(ClassType), typeof(Type), typeof(ChangeAttachedPropertyAction), new PropertyMetadata(null));

		/// <summary>
		/// 附加属性名称
		/// </summary>
		public string PropertyName
		{
			get => (string)GetValue(PropertyNameProperty);
			set => SetValue(PropertyNameProperty, value);
		}
		public static readonly DependencyProperty PropertyNameProperty = DependencyProperty.Register(
			nameof(PropertyName), typeof(string), typeof(ChangeAttachedPropertyAction), new PropertyMetadata(""));

		/// <summary>
		/// 需要设置的值
		/// </summary>
		public object Value
		{
			get => GetValue(ValueProperty);
			set => SetValue(ValueProperty, value);
		}
		public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
			nameof(Value), typeof(object), typeof(ChangeAttachedPropertyAction), new PropertyMetadata(null));

		/// <inheritdoc />
		protected override void Invoke(object parameter)
		{
			if(ClassType == null)
			{
				throw new ArgumentNullException(nameof(ClassType));
			}

			if(string.IsNullOrWhiteSpace(PropertyName))
			{
				throw new ArgumentNullException(nameof(PropertyName));
			}

			MethodInfo setter = ClassType.GetMethod($"Set{PropertyName}", BindingFlags.Static | BindingFlags.Public)
				?? throw new ArgumentException($"Method {PropertyName} not found in {ClassType}");

			Type parameterType = setter.GetParameters()[1].ParameterType;
			if(parameterType.IsInstanceOfType(Value))
			{
				setter.Invoke(null, new[] { Target, Value });
				return;
			}

			var tc = TypeDescriptor.GetConverter(parameterType);
			if(Value != null && tc.CanConvertFrom(Value.GetType()))
			{
				if(Target == null)
					return;

				setter.Invoke(null, new[] { Target, tc.ConvertFrom(Value) });
				return;
			}

			throw new ArgumentException($"Cannot convert {Value?.GetType()} to {parameterType}");
		}
	}
}
